"use strict";
exports.__esModule = true;
exports.getResourceFlow = exports.getCLS = exports.getFID = exports.getLCP = exports.getFCP = exports.initFMP = exports.observe = exports.afterLoad = void 0;
var store_1 = require("./store");
var afterLoad = function (callback) {
    if (document.readyState === 'complete') {
        setTimeout(callback);
    }
    else {
        window.addEventListener('pageshow', callback, {
            once: true,
            capture: true
        });
    }
};
exports.afterLoad = afterLoad;
var observe = function (type, callback) {
    var _a;
    // 类型合规，就返回 observe
    if ((_a = PerformanceObserver.supportedEntryTypes) === null || _a === void 0 ? void 0 : _a.includes(type)) {
        var ob = new PerformanceObserver(function (l) {
            return l.getEntries().map(callback);
        });
        ob.observe({ type: type, buffered: true });
        return ob;
    }
    return undefined;
};
exports.observe = observe;
var initFMP = function () {
    new MutationObserver(function (records) {
        // 对当前的 document 进行计算评分
        // 或者对 records.addedNodes的每个 node 元素，计算评分累加;每次遍历元素还需要判断此元素是否在可视区域
    }).observe(document, { childList: true, subtree: true });
};
exports.initFMP = initFMP;
// 初始化 FP 的获取以及返回
var getFP = function () {
    var entry = performance.getEntriesByName('first-paint')[0];
    return entry;
};
// 获取 FCP
var getFCP = function () {
    var entry = performance.getEntriesByName('first-contentful-paint')[0];
    return entry;
};
exports.getFCP = getFCP;
// 获取 LCP
var getLCP = function (entryHandler) {
    return (0, exports.observe)('largest-contentful-paint', entryHandler);
};
exports.getLCP = getLCP;
// 获取 FID
var getFID = function (entryHandler) {
    return (0, exports.observe)('first-input', entryHandler);
};
exports.getFID = getFID;
// 获取 CLS
var getCLS = function (entryHandler) {
    return (0, exports.observe)('layout-shift', entryHandler);
};
exports.getCLS = getCLS;
// 获取 NT
var getNavigationTiming = function () {
    var resolveNavigationTiming = function (entry) {
        var domainLookupStart = entry.domainLookupStart, domainLookupEnd = entry.domainLookupEnd, connectStart = entry.connectStart, connectEnd = entry.connectEnd, secureConnectionStart = entry.secureConnectionStart, requestStart = entry.requestStart, responseStart = entry.responseStart, responseEnd = entry.responseEnd, domInteractive = entry.domInteractive, domContentLoadedEventEnd = entry.domContentLoadedEventEnd, loadEventStart = entry.loadEventStart, fetchStart = entry.fetchStart;
        return {
            // 关键时间点
            FP: responseEnd - fetchStart,
            TTI: domInteractive - fetchStart,
            DomReady: domContentLoadedEventEnd - fetchStart,
            Load: loadEventStart - fetchStart,
            FirseByte: responseStart - domainLookupStart,
            // 关键时间段
            DNS: domainLookupEnd - domainLookupStart,
            TCP: connectEnd - connectStart,
            SSL: secureConnectionStart ? connectEnd - secureConnectionStart : 0,
            TTFB: responseStart - requestStart,
            Trans: responseEnd - responseStart,
            DomParse: domInteractive - responseEnd,
            Res: loadEventStart - domContentLoadedEventEnd
        };
    };
    var navigation = 
    // W3C Level2  PerformanceNavigationTiming
    // 使用了High-Resolution Time，时间精度可以达毫秒的小数点好几位。
    performance.getEntriesByType('navigation').length > 0
        ? performance.getEntriesByType('navigation')[0]
        : performance.timing; // W3C Level1  (目前兼容性高，仍然可使用，未来可能被废弃)。
    return resolveNavigationTiming(navigation);
};
// 获取 RF
var getResourceFlow = function (resourceFlow) {
    var entryHandler = function (entry) {
        var name = entry.name, transferSize = entry.transferSize, initiatorType = entry.initiatorType, startTime = entry.startTime, responseEnd = entry.responseEnd, domainLookupEnd = entry.domainLookupEnd, domainLookupStart = entry.domainLookupStart, connectStart = entry.connectStart, connectEnd = entry.connectEnd, secureConnectionStart = entry.secureConnectionStart, responseStart = entry.responseStart, requestStart = entry.requestStart;
        resourceFlow.push({
            // name 资源地址
            name: name,
            // transferSize 传输大小
            transferSize: transferSize,
            // initiatorType 资源类型
            initiatorType: initiatorType,
            // startTime 开始时间
            startTime: startTime,
            // responseEnd 结束时间
            responseEnd: responseEnd,
            // 贴近 Chrome 的近似分析方案，受到跨域资源影响
            dnsLookup: domainLookupEnd - domainLookupStart,
            initialConnect: connectEnd - connectStart,
            ssl: connectEnd - secureConnectionStart,
            request: responseStart - requestStart,
            ttfb: responseStart - requestStart,
            contentDownload: responseStart - requestStart
        });
    };
    return (0, exports.observe)('resource', entryHandler);
};
exports.getResourceFlow = getResourceFlow;
// 初始化入口，外部调用只需要 new WebVitals();
var WebVitals = /** @class */ (function () {
    function WebVitals(engineInstance) {
        var _this = this;
        // 性能数据的上报策略
        this.perfSendHandler = function () {
            // 如果你要监听 FID 数据。你就需要等待 FID 参数捕获完成后进行上报;
            // 如果不需要监听 FID，那么这里你就可以发起上报请求了;
        };
        // 初始化 FP 的获取以及返回
        this.initFP = function () {
            var entry = getFP();
            var metrics = {
                startTime: entry === null || entry === void 0 ? void 0 : entry.startTime.toFixed(2),
                entry: entry
            };
            _this.metrics.set(store_1.metricsName.FP, metrics);
        };
        // 初始化 FCP 的获取以及返回
        this.initFCP = function () {
            var entry = (0, exports.getFCP)();
            var metrics = {
                startTime: entry === null || entry === void 0 ? void 0 : entry.startTime.toFixed(2),
                entry: entry
            };
            _this.metrics.set(store_1.metricsName.FCP, metrics);
        };
        // 初始化 LCP 的获取以及返回
        this.initLCP = function () {
            var entryHandler = function (entry) {
                var metrics = {
                    startTime: entry === null || entry === void 0 ? void 0 : entry.startTime.toFixed(2),
                    entry: entry
                };
                _this.metrics.set(store_1.metricsName.LCP, metrics);
            };
            (0, exports.getLCP)(entryHandler);
        };
        // 初始化 FID 的获取 及返回
        this.initFID = function () {
            var entryHandler = function (entry) {
                var metrics = {
                    delay: entry.processingStart - entry.startTime,
                    entry: entry
                };
                _this.metrics.set(store_1.metricsName.FID, metrics);
            };
            (0, exports.getFID)(entryHandler);
        };
        // 初始化 CLS 的获取以及返回
        this.initCLS = function () {
            var clsValue = 0;
            var clsEntries = [];
            var sessionValue = 0;
            var sessionEntries = [];
            var entryHandler = function (entry) {
                if (!entry.hadRecentInput) {
                    var firstSessionEntry = sessionEntries[0];
                    var lastSessionEntry = sessionEntries[sessionEntries.length - 1];
                    // 如果条目与上一条目的相隔时间小于 1 秒且
                    // 与会话中第一个条目的相隔时间小于 5 秒，那么将条目
                    // 包含在当前会话中。否则，开始一个新会话。
                    if (sessionValue &&
                        entry.startTime - lastSessionEntry.startTime < 1000 &&
                        entry.startTime - firstSessionEntry.startTime < 5000) {
                        sessionValue += entry.value;
                        sessionEntries.push(entry);
                    }
                    else {
                        sessionValue = entry.value;
                        sessionEntries = [entry];
                    }
                    // 如果当前会话值大于当前 CLS 值，
                    // 那么更新 CLS 及其相关条目。
                    if (sessionValue > clsValue) {
                        clsValue = sessionValue;
                        clsEntries = sessionEntries;
                        // 记录 CLS 到 Map 里
                        var metrics = {
                            entry: entry,
                            clsValue: clsValue,
                            clsEntries: clsEntries
                        };
                        _this.metrics.set(store_1.metricsName.CLS, metrics);
                    }
                }
            };
            (0, exports.getCLS)(entryHandler);
        };
        // 初始化 NT 的获取以及返回
        this.initNavigationTiming = function () {
            var navigationTiming = getNavigationTiming();
            var metrics = navigationTiming;
            _this.metrics.set(store_1.metricsName.NT, metrics);
        };
        // 初始化 RF 的获取以及返回
        this.initResourceFlow = function () {
            var resourceFlow = [];
            var resObserve = (0, exports.getResourceFlow)(resourceFlow);
            var stopListening = function () {
                if (resObserve) {
                    resObserve.disconnect();
                }
                var metrics = resourceFlow;
                _this.metrics.set(store_1.metricsName.RF, metrics);
            };
            // 当页面 pageshow 触发时，中止
            window.addEventListener('pageshow', stopListening, {
                once: true,
                capture: true
            });
        };
        this.engineInstance = engineInstance;
        this.metrics = new store_1["default"]();
        this.initLCP();
        this.initCLS();
        this.initResourceFlow();
        // 这里的 FP/FCP/FID需要在页面成功加载了再进行获取
        (0, exports.afterLoad)(function () {
            _this.initNavigationTiming();
            _this.initFP();
            _this.initFCP();
            _this.initFID();
            _this.perfSendHandler();
        });
    }
    return WebVitals;
}());
exports["default"] = WebVitals;
